import os
import subprocess
import tempfile
import json
from pathlib import Path

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from logger import log
from version import resource_path


def _find_wkhtmltopdf() -> str | None:
    """
    仅使用项目内置的 wkhtmltopdf 可执行文件。
    路径解析使用 version.resource_path，兼容 PyInstaller（sys._MEIPASS）。
    """
    try:
        p = resource_path("wkhtmltopdf.exe")
        # 统一路径为绝对路径，避免分隔符差异带来的问题
        abs_path = str(Path(p).resolve())
        if os.path.isfile(abs_path):
            return abs_path
        # 未找到时直接返回 None，不再查询其他位置
        return None
    except Exception:
        return None


def _find_sumatrapdf() -> str | None:
    """
    仅使用项目内置的 SumatraPDF-3.5.2-32.exe 可执行文件。
    路径解析使用 version.resource_path，兼容 PyInstaller（sys._MEIPASS）。
    """
    try:
        p = resource_path("SumatraPDF-3.5.2-32.exe")
        # 统一路径为绝对路径，避免分隔符差异带来的问题
        abs_path = str(Path(p).resolve())
        if os.path.isfile(abs_path):
            return abs_path
        # 未找到时直接返回 None，不再查询其他位置
        return None
    except Exception:
        return None


def _convert_html_to_pdf(data: dict, output_pdf_path: str) -> bool:
    """
    Convert HTML string to PDF using wkhtmltopdf.
    - Supports standard page sizes via --page-size and --orientation
    - Supports custom width/height via --page-width/--page-height (mm)
    - Applies uniform margins via --margin-*
    """
    wk = _find_wkhtmltopdf()
    if not wk:
        log.error("未找到 wkhtmltopdf，可执行文件不存在或未安装")
        return False

    # Write HTML to a temp file
    try:
        with tempfile.NamedTemporaryFile(delete=False, suffix=".html", mode="w", encoding="utf-8") as tf:
            tmp_html_path = tf.name
            # Note: wkhtmltopdf largely ignores @page size; CLI flags control layout.
            tf.write(data.get('html', ''))
    except Exception as e:
        log.exception(f"写入临时HTML失败: {e}")
        return False

    cmd = [
        wk,
        "--quiet",
        "--encoding", "utf-8",
        "--print-media-type",
        "--enable-local-file-access",
        "--margin-top", f"{data.get('paper_margin_mm_top', 0)}mm",
        "--margin-right", f"{data.get('paper_margin_mm_right', 0)}mm",
        "--margin-bottom", f"{data.get('paper_margin_mm_bottom', 0)}mm",
        "--margin-left", f"{data.get('paper_margin_mm_left', 0)}mm",
    ]

    try:
        if data.get('paper_width_mm', 0) > 0 and data.get('paper_height_mm', 0) > 0:
            cmd.extend(["--page-width", f"{data.get('paper_width_mm', 0)}mm", "--page-height", f"{data.get('paper_height_mm', 0)}mm"])
        else:
            paper_size = data.get('paper_size', '')
            paper_size = 'A4' if paper_size == '' else paper_size
            cmd.extend(["--page-size", paper_size])

        paper_orientation = data.get('paper_orientation', '')
        if paper_orientation != '':
            cmd.extend(["--orientation", paper_orientation])

        cmd.extend([tmp_html_path, output_pdf_path])

        log.info(f"调用 wkhtmltopdf 生成PDF: {' '.join(cmd)}")
        # Windows 下隐藏窗口
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = 0  # SW_HIDE
        result = subprocess.run(cmd, startupinfo=startupinfo, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, timeout=60)
        if result.returncode != 0:
            stderr = result.stderr.decode(errors="ignore")
            log.error(f"生成PDF失败，返回码{result.returncode}, stderr={stderr}")
            return False
        if not os.path.isfile(output_pdf_path) or os.path.getsize(output_pdf_path) == 0:
            log.error("生成的PDF不存在或大小为0")
            return False
        return True
    except subprocess.TimeoutExpired:
        log.error("生成PDF超时")
        return False
    except Exception as e:
        log.exception(f"生成PDF出现异常: {e}")
        return False
    # finally:
    #     try:
    #         os.unlink(tmp_html_path)
    #     except Exception:
    #         pass


def _print_pdf(data: dict, pdf_path: str) -> bool:
    """Print PDF to system default printer using native OS tools.
    - Windows: SumatraPDF (recommended), fallback to powershell
    - macOS/Linux: lp or lpr
    """
    if not os.path.isfile(pdf_path):
        log.error("待打印的PDF文件不存在")
        return False

    try:
        if os.name == "nt":
            sumatra_pdf = _find_sumatrapdf()
            if sumatra_pdf:
                # Use SumatraPDF to print and close silently
                cmd = [sumatra_pdf, "-print-to-default"]
                # 处理纸张方向
                print_settings = data.get('print_settings', '')
                if print_settings != '':
                    cmd.extend(["-print-settings", print_settings])

                cmd.extend(["-silent", "-exit-on-print", pdf_path])
                log.info(f"调用 SumatraPDF 打印: {' '.join(cmd)}")

                # Windows 下隐藏窗口
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                startupinfo.wShowWindow = 0  # SW_HIDE

                result = subprocess.run(cmd, startupinfo=startupinfo, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, timeout=60)
                if result.returncode != 0:
                    log.error(f"SumatraPDF 打印失败，返回码{result.returncode}, stderr={result.stderr.decode(errors='ignore')}")
                    return False
                return True
            else:
                # Fallback to PowerShell if SumatraPDF is not found
                log.warning("未找到 SumatraPDF，回退到 PowerShell 打印")
                return False
            return True
        else:
            log.error(f"暂不支持其他平台打印")
            return False
    except subprocess.TimeoutExpired:
        log.error("打印命令执行超时")
        return False
    except Exception as e:
        log.exception(f"打印PDF出现异常: {e}")
        return False


def _handle_print_request(data: dict):
    """Process printing request and return (status_code, response_dict)."""
    # Generate PDF in temp path
    try:
        with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as pf:
            pdf_path = pf.name
    except Exception as e:
        return 500, {"error": f"create temp pdf failed: {e}"}

    ok_pdf = _convert_html_to_pdf(data, pdf_path)
    if not ok_pdf:
        try:
            os.unlink(pdf_path)
        except Exception:
            pass
        return 500, {"error": "convert to pdf failed"}

    # Print PDF to default printer
    ok_print = _print_pdf(data, pdf_path)
    # Cleanup PDF afterwards
    # try:
    #     os.unlink(pdf_path)
    # except Exception:
    #     pass

    if not ok_print:
        return 500, {"error": "print failed"}

    return 200, {"status": "ok"}


class PrinterRequestHandler(BaseHTTPRequestHandler):
    def _add_cors_headers(self):
        # Allow cross-origin requests
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "*")

    def _send_json(self, status_code: int, payload: dict):
        body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
        self.send_response(status_code)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self._add_cors_headers()
        self.end_headers()
        self.wfile.write(body)

    def _send_text(self, status_code: int, text: str):
        body = text.encode("utf-8")
        self.send_response(status_code)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self._add_cors_headers()
        self.end_headers()
        self.wfile.write(body)

    def do_GET(self):
        if self.path == "/":
            self._send_text(200, "打印服务")
        else:
            self._send_json(404, {"error": "not found"})

    def do_OPTIONS(self):
        # Preflight requests for CORS
        self.send_response(204)
        self._add_cors_headers()
        self.end_headers()

    def do_POST(self):
        if self.path == "/print":
            try:
                content_length = int(self.headers.get("Content-Length", "0"))
            except Exception:
                content_length = 0
            body = b""
            if content_length > 0:
                body = self.rfile.read(content_length)
            try:
                data = json.loads(body.decode("utf-8")) if body else {}
            except Exception:
                return self._send_json(400, {"error": "invalid json"})

            status, resp = _handle_print_request(data or {})
            self._send_json(status, resp)
        else:
            self._send_json(404, {"error": "not found"})


def run_http_server(port: int = 9001):
    log.info(f"启动打印HTTP服务端口 {port}")
    server = ThreadingHTTPServer(("0.0.0.0", port), PrinterRequestHandler)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()


def main():
    """
    Entry point called by main.py. Blocks on the printer HTTP server.
    """
    try:
        run_http_server(9001)
    except Exception as e:
        log.exception(f"打印服务启动失败: {e}")


if __name__ == "__main__":
    main()
